﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework.Graphics;
using GameEngine.Scenes;
using Microsoft.Xna.Framework;
using GuiLibrary.Components;

namespace GuiLibrary.layouts
{
    public abstract class Layout
    {
        public int marginTop, marginLeft, marginRight, marginDown;
        public IOnElementSelected onComponentSelectedListener;
        public IOnElementPointed onComponentPointedListener;
        public IOnCancel onCancelListener;
        private bool _flashPointedElement;
        public bool flashPointedElement 
        {
            get { return _flashPointedElement; }
            set 
            {
                _flashPointedElement = value;
                if (value)
                    refreshPointedComponent();
                else
                    turnOfPointedComponent();
            }
        }
        private int _pointedElement;
        public int pointedElement 
        {
            get { return _pointedElement; }
            set
            {
                int compAmount = getComponents().Count;
                if (compAmount > 0 && (value < 0 || value >= compAmount))
                    throw new IndexOutOfRangeException();
                else
                    _pointedElement = value;
            }
        }
        public Panel parentPanel;
        public List<Component> getComponents() 
        {
            return parentPanel.components;
        }

        public Layout(Panel parentPanel, int[] margins, IOnElementSelected onComponentSelectedListener, IOnCancel onCancelListener, IOnElementPointed onComponentPointedListener) 
        {
            this.onComponentSelectedListener = onComponentSelectedListener;
            this.onCancelListener = onCancelListener;
            this.onComponentPointedListener = onComponentPointedListener;
            this.parentPanel = parentPanel;
            this.pointedElement = 0;
            this.flashPointedElement = true;
            this.marginLeft = margins[0]; this.marginTop = margins[1]; this.marginRight = margins[2]; this.marginDown = margins[3];
        }
        public Layout(Panel parentPanel) :
            this(parentPanel, new int[] { 0, 0, 0, 0 }, null, null, null)
        { }

        public void checkEnterCancel(Cursor cursor) 
        {
            List<Component> components = getComponents();
            if (cursor == Cursor.ENTER && onComponentSelectedListener != null && components.Count > 0 && getCurrentComponent() is ISelectableComponent)
                onComponentSelectedListener.onSelectedElement(((ISelectableComponent)getCurrentComponent()).getAssociatedElement());
            if (cursor == Cursor.CANCEL && onCancelListener != null)
                onCancelListener.onCancel();
        }
        public virtual void moveCursor(Cursor cursor) 
        {
            checkEnterCancel(cursor);
            foreach (Component component in getComponents())
                if(component != null) component.moveCursor(cursor);
        }

        public virtual void draw(SpriteBatch sb) 
        {
            foreach (Component component in getComponents())
            {
                if(component != null) component.draw(sb);
            }
        }

        public virtual void update(int dTime)
        {
            foreach (Component component in getComponents())
            {
                if(component != null) component.update(dTime);
            }
        }

        public virtual void refresh() 
        {
            foreach (Component component in getComponents())
            {
                component.refresh();
            }
            refreshPointedComponent();
        }

        public void refreshPointedComponent() 
        {
            if (flashPointedElement && getComponents().Count > 0 && getCurrentComponent() is ISelectableComponent)
            {
                ISelectableComponent selComponent = (ISelectableComponent)getCurrentComponent();
                selComponent.onComponentPointed();
            }
        }
        public void turnOfPointedComponent()
        {
            if (getComponents().Count > 0 && getCurrentComponent() is ISelectableComponent)
            {
                ISelectableComponent selComponent = (ISelectableComponent)getCurrentComponent();
                selComponent.onComponentUnpointed();
            }
        }

        public virtual void changePointedComponent(int nextPosition) 
        {
            if (pointedElement != nextPosition) 
            {
                if (nextPosition < 0)
                    nextPosition = 0;
                else if (nextPosition >= getComponents().Count)
                    nextPosition = getComponents().Count - 1;

                Component prev = getCurrentComponent();
                ISelectableComponent prevSel = null, nextSel = null;
                if (prev is ISelectableComponent)
                {
                    prevSel = (ISelectableComponent)prev;
                    prevSel.onComponentUnpointed();
                }
                pointedElement = nextPosition;
                Component next = getCurrentComponent();
                if (next is ISelectableComponent)
                {
                    nextSel = (ISelectableComponent)next;
                    nextSel.onComponentPointed();
                    if(onComponentPointedListener!=null)onComponentPointedListener.onElementPointed(nextSel.getAssociatedElement());
                }
            }
        }

        public virtual Component getCurrentComponent() 
        {
            if (getComponents().Count > 0)
                return getComponents()[pointedElement];
            else
                return null;
        }
        public abstract Vector2 getComponentSize(Component component);

        public enum Cursor { 
            UP,
            LEFT,
            DOWN,
            RIGHT,
            ENTER,
            CANCEL,
            NONE
        }

        public abstract bool canMove(Cursor cursor);
        public abstract void eraseComponent(int componentIndex);
    }
}
